/*jslint bitwise: true, eqeqeq: true, immed: true, newcap: true, nomen: true,
  onevar: true, plusplus: true, regexp: true, strict: true, undef: true,
  white: true, indent: 4 */
"use strict";
this.WebLayout2 = function (element) {
    this.modules = [];
    this.contentElement = element;
    this.mouseX = undefined;
    this.mouseY = undefined;
    this.threadPriority = 30;
    this.threadTimer = null;
    this.threadDelay = 0;
    this.thread = function () {};
    if (this.AJAXlib2 instanceof Function) {
        this.ajaxLib = new this.AJAXlib2();
    }
    this.init(this);
};

this.WebLayout2.prototype = (function () {
    var init, addModule, getModule, setThreadPriority, moduleEvent, redraw,
            addEventListener, removeEventListener, dispatchEvent, xPath, css,
            json, getOffset, getStyle, getThread, setUpThread, document,
            setUpResizeHandler, setUpMouseHandler, preventDefault,
            stopPropagation, encodeJSON;

    document = this.document;

    init = function (that) {
        that.thread = getThread(that);
        setUpThread(that);
        setUpResizeHandler(that);
        setUpMouseHandler(that);
    };

    addModule = function (module) {
        this.modules[module.NAME] = module;
        module.init(this);
    };

    getModule = function (module) {
        return this.modules[module];
    };

    setThreadPriority = function (that, priority) {
        this.clearInterval(that.threadTimer);
        that.threadPriority = priority;
        that.threadTimer = this.setInterval(that.thread, priority);
    };

    moduleEvent = function (event, details) {
        var handler, module;
        handler = 'on' + event.charAt(0).toUpperCase() + event.substring(1);
        for (module in this.modules) {
            if (this.modules.hasOwnProperty(module) &&
                    (this.modules[module][handler] !== undefined) &&
                    (this.modules[module][handler] instanceof Function)) {
                this.modules[module][handler](this, details);
            }
        }
    };

    redraw = function (details) {
        this.moduleEvent('redraw', details);
    };

    addEventListener = function (target, event, listener) {
        if (target.addEventListener instanceof Function) {
            target.addEventListener(event, listener, false);
        } else {
            target.attachEvent('on' + event, listener);
        }
    };

    removeEventListener = function (target, event, listener) {
        if (target.removeEventListener instanceof Function) {
            target.removeEventListener(event, listener, false);
        } else {
            target.detachEvent('on' + event, listener);
        }
    };

    dispatchEvent = function (target, event) {
        var eventObject;
        if (target.dispatchEvent instanceof Function) {
            eventObject = document.createEvent('Events');
            eventObject.initEvent(event, true, true);
            return target.dispatchEvent(eventObject);
        } else {
            return target.fireEvent('on' + event);
        }
    };

    preventDefault = function (event) {
        if (event.preventDefault instanceof Function) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    };

    stopPropagation = function (event) {
        if (event.stopPropagation instanceof Function) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    };

    json = function (jsonString) {
        if ((this.JSON instanceof Object) &&
                (this.JSON.parse instanceof Function)) {
            return this.JSON.parse(jsonString);
        }
        try {
            // Source: http://www.ietf.org/rfc/rfc4627.txt?number=4627
            return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
                    jsonString.replace(/"(\\([^a]|a)|[^"\\])*"/g, ''))) &&
                    eval('(' + jsonString + ')');
        } catch (e) {}
        return false;
    };

    encodeJSON = function (value) {
        if ((this.JSON instanceof Object) &&
                (this.JSON.stringify instanceof Function)) {
            return this.JSON.stringify(value);
        }
        throw new Error('JSON encoder is not available');
    };

    getOffset = function (element) {
        var parent;
        if (element.offsetParent !== null) {
            parent = getOffset(element.offsetParent);
            return {
                height: element.offsetHeight,
                left: element.offsetLeft + parent.left,
                top: element.offsetTop + parent.top,
                width: element.offsetWidth
            };
        } else {
            return {
                height: element.offsetHeight,
                left: element.offsetLeft,
                top: element.offsetTop,
                width: element.offsetWidth
            };
        }
    };

    getStyle = function (element) {
        if (element.currentStyle !== undefined) {
            return element.currentStyle;
        } else {
            return document.defaultView.getComputedStyle(element, null);
        }
    };

    xPath = function (xpath, context) {
        var result, elements, element;
        if (context === undefined) {
            context = this.contentElement;
        }
        result = document.evaluate(xpath, context, null,
                this.XPathResult.ANY_TYPE, null);
        elements = [];
        element = result.iterateNext();
        while ((element !== undefined) && (element !== null)) {
            elements.push(element);
            element = result.iterateNext();
        }
        return elements;
    };

    css = function (selector) {
        return document.querySelectorAll(selector);
    };

    setUpMouseHandler = function (that) {
        that.addEventListener(document, 'mousemove', function (e) {
            that.mouseX = e.clientX + document.body.scrollLeft;
            that.mouseY = e.clientY + document.body.scrollTop;
        });
    };

    setUpResizeHandler = function (that) {
        that.addEventListener(this.window, 'resize', function () {
            if (this.window.innerWidth !== undefined) {
                that.moduleEvent('resize', {
                    width: this.window.innerWidth + this.window.pageXOffset,
                    height: this.window.innerHeight + this.window.pageYOffset
                });
            } else {
                that.moduleEvent('resize', {
                    width: document.body.clientWidth + document.body.scrollTop,
                    height: document.body.clientHeight +
                            document.body.scrollLeft
                });
            }
        });
    };

    setUpThread = function (that) {
        that.threadTimer = this.setInterval(that.thread, that.threadPriority);
    };

    getThread = function (that) {
        var lastInvocation = (new Date()).getTime();
        return function () {
            var module, currentTime;
            currentTime = (new Date()).getTime();
            that.threadDelay = currentTime - lastInvocation;
            lastInvocation = currentTime;
            for (module in that.modules) {
                if (that.modules.hasOwnProperty(module)) {
                    that.modules[module].thread(that);
                }
            }
        };
    };

    return {
        init: init,
        addModule: addModule,
        getModule: getModule,
        setThreadPriority: function (priority) {
            setThreadPriority(this, priority);
        },
        moduleEvent: moduleEvent,
        redraw: redraw,
        addEventListener: addEventListener,
        removeEventListener: removeEventListener,
        dispatchEvent: dispatchEvent,
        preventDefault: preventDefault,
        stopPropagation: stopPropagation,
        xPath: xPath,
        css: css,
        json: json,
        encodeJSON: encodeJSON,
        getOffset: getOffset,
        getStyle: getStyle
    };
}());

if (this.AJAXlib2 instanceof Function) {
    this.WebLayout2.prototype.AJAXlib2 = this.AJAXlib2;
}

if (this.JSON instanceof Object) {
    this.WebLayout2.prototype.JSON = this.JSON;
}

if (this.XPathResult instanceof Object) {
    this.WebLayout2.prototype.XPathResult = this.XPathResult;
}